package com.test.netty.http;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.test.netty.http.pojo.User;
import com.test.netty.http.serialize.impl.JSONSerializer;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.util.AsciiString;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.Charsets;

import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final Logger logger = LoggerFactory.getLogger(HttpHelloWorldServerHandler.class);

    private static final String FAVICON_ICO = "/favicon.ico";
    private static final AsciiString CONTENT_TYPE = AsciiString.of("Content-Type");
    private static final AsciiString CONTENT_LENGTH = AsciiString.of("Content-Length");
    private static final AsciiString CONNECTION = AsciiString.of("Connection");
    private static final AsciiString KEEP_ALIVE = AsciiString.of("keep-alive");

    @Override
    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if (msg instanceof HttpRequest) {
            HttpRequest request = (HttpRequest) msg;
            HttpHeaders headers = request.headers();
            logger.info("http uri: " + request.uri());
            if (FAVICON_ICO.equals(request.uri())) {
                return;
            }
            HttpMethod method = request.method();
            if (method.equals(HttpMethod.GET)) {
                get(ctx, request);
            } else if (method.equals(HttpMethod.POST)) {
                post(ctx, request);
            }
        }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    /**
     * 简单处理常用几种 Content-Type 的 POST 内容（可自行扩展）
     *
     * @throws Exception
     */
    private void dealWithContentType(FullHttpRequest fullRequest) throws Exception {
        String contentType = this.getContentType(fullRequest.headers());
        //可以使用HttpJsonDecoder
        if (contentType.equals("application/json")) {
            String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
            JSONObject obj = JSON.parseObject(jsonStr);
            for (Entry<String, Object> item : obj.entrySet()) {
                logger.info(item.getKey() + "=" + item.getValue().toString());
            }
        } else if (contentType.equals("application/x-www-form-urlencoded")) {
            //方式一：使用 QueryStringDecoder
            String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
            QueryStringDecoder queryDecoder = new QueryStringDecoder(jsonStr, false);
            Map<String, List<String>> uriAttributes = queryDecoder.parameters();
            for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
                for (String attrVal : attr.getValue()) {
                    logger.info(attr.getKey() + "=" + attrVal);
                }
            }
        } else if (contentType.equals("multipart/form-data")) {
            //TODO 用于文件上传
        } else {
            //do nothing...
        }
    }

    private String getContentType(HttpHeaders headers) {
        String typeStr = headers.get("Content-Type");
        String[] list = typeStr.split(";");
        return list[0];
    }

    private void get(ChannelHandlerContext ctx, HttpRequest req) {
        QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(), Charsets.toCharset(CharEncoding.UTF_8));
        Map<String, List<String>> uriAttributes = queryDecoder.parameters();
        //此处仅打印请求参数（你可以根据业务需求自定义处理）
        for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
            for (String attrVal : attr.getValue()) {
                logger.info(attr.getKey() + "=" + attrVal);
            }
        }
        User user = new User();
        user.setUserName("sanshengshui");
        user.setDate(new Date());
        user.setMethod("get");

        JSONSerializer jsonSerializer = new JSONSerializer();
        byte[] content = jsonSerializer.serialize(user);

        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(content));
        response.headers().set(CONTENT_TYPE, "text/plain");
        response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());

        boolean keepAlive = HttpUtil.isKeepAlive(req);
        if (!keepAlive) {
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            response.headers().set(CONNECTION, KEEP_ALIVE);
            ctx.write(response);
        }
    }

    private void post(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        //POST请求,由于你需要从消息体中获取数据,因此有必要把msg转换成FullHttpRequest
        FullHttpRequest fullRequest = (FullHttpRequest) msg;
        //根据不同的Content_Type处理body数据
        dealWithContentType(fullRequest);

        User user = new User();
        user.setUserName("sanshengshui");
        user.setDate(new Date());
        user.setMethod("post");
        user.setMethod("post");

        JSONSerializer jsonSerializer = new JSONSerializer();
        byte[] content = jsonSerializer.serialize(user);

        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(content));
        response.headers().set(CONTENT_TYPE, "text/plain");
        response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());

        boolean keepAlive = HttpUtil.isKeepAlive(fullRequest);
        if (!keepAlive) {
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            response.headers().set(CONNECTION, KEEP_ALIVE);
            ctx.write(response);
        }
    }
}
